require 'log4r'
require 'log4r/yamlconfigurator'
# require 'log4r/outputter/fileoutputter'
require 'log4r-gelf'

module Skit
  module Log4r
    module Gelf
      class Configuration
        DEFAULT_LEVELS = %w(DEBUG INFO NOTICE WARN ERROR FATAL)

        DEFAULT_LOGGERS = {
          development: { level: :debug, trace: true, outputters: [:stdout] },
          test: { level: :debug, trace: true, outputters: [:stdout] },
        }

        DEFAULT_OUTPUTTERS = {
          stdout: { type: 'StdoutOutputter', formatter: { pattern: '%m', type: 'PatternFormatter' } }
        }

        # %w(DEBUG INFO NOTICE WARN ERROR FATAL)
        attr_accessor :levels

        # {
        #   production: { level: :debug, trace: false, outputters: [:gelf_info, :gelf_debug] },
        #   development: { level: :debug, trace: true, outputters: [:stdout] },
        #   test: { level: :debug, trace: true, outputters: [:stdout] },
        # }
        attr_accessor :loggers

        # {
        #   stdout: { type: 'StdoutOutputter', formatter: { pattern: '%m', type: 'PatternFormatter' } }
        # }
        attr_accessor :outputters

        DEFAULT_GELF_PORT = 12201

        # {
        #   info: { server: 'graylog.localhost', port: 12201 },
        #   debug: { server: 'graylog2.localhost', port: 12201 }
        # }
        attr_accessor :gelf_servers

        attr_accessor :gelf_facility

        # { country: 'US', something: 'something' }
        # NOTE: environment will be set automatically!
        attr_accessor :gelf_mdcs

        def initialize
          @levels = default_levels
          @loggers = default_loggers
          @outputters = default_outputters
          @gelf_servers = []
          @gelf_facility = nil
          @gelf_mdcs = {}
        end

        def default_levels
          DEFAULT_LEVELS
        end

        def default_loggers
          DEFAULT_LOGGERS
        end

        def default_outputters
          DEFAULT_OUTPUTTERS
        end

        def to_log4r
          result = {
            'pre_config' => {
              'custom_levels' => levels
            },
            'loggers' => [],
            'outputters' => []
          }

          result['loggers'] += loggers.map do |name, logger|
            build_logger(name, logger[:level], logger[:trace], logger[:outputters])
          end

          production_outputters = gelf_servers.map { |name, _| "gelf_#{name}" }
          result['loggers'] << build_logger('production', levels.first, false, production_outputters)

          result['outputters'] += outputters.map do |name, outputter|
            build_outputter(name, outputter[:type], outputter[:formatter])
          end

          result['outputters'] += gelf_servers.map do |name, outputter|
            build_gelf_outputter("gelf_#{name}", name, outputter[:server], outputter[:port])
          end

          result
        end

        def validate!
          raise ArgumentError, 'No gelf facility set' if gelf_facility.nil?
          raise ArgumentError, 'No gelf servers set' if gelf_servers.size.zero?
        end

        def configure!(environment)
          ::Log4r::MDC.put(:environment, environment)
          gelf_mdcs.each { |key, value| ::Log4r::MDC.put(key, value) }

          # pp to_log4r.to_yaml

          ::Log4r::YamlConfigurator.decode_yaml(to_log4r)
          ::Log4r::Logger[environment]
        end

        private

        def build_logger(name, level, trace, outputters)
          {
            'name' => name.to_s,
            'level' => level.to_s.upcase,
            'trace' => trace,
            'outputters' => outputters.map { |outputter| outputter.to_s }
          }
        end

        def build_outputter(name, type, formatter)
          {
            'name' => name.to_s,
            'type' => type,
            'formatter' => build_formatter(formatter[:type], formatter[:pattern])
          }
        end

        def build_gelf_outputter(name, level, server, port)
          outputter = build_outputter(name, 'SimpleGelfOutputter', { type: 'PatternFormatter', pattern: '%m' })
          outputter.tap do |o|
            o['level'] = level.to_s.upcase
            o['gelf_server'] = server
            o['gelf_port'] = port || DEFAULT_GELF_PORT
            o['facility'] = gelf_facility
            o['mapped_context_prefix'] = ''
          end
        end

        def build_formatter(type, pattern)
          { 'type' => type, 'pattern' => pattern }
        end
      end
    end
  end
end
